﻿using System;
using System.Windows.Forms;
using BusinessLogic.BusinessEntity.Entities;
using BusinessLogic.Cryptography;
using BankApplication.BusinessLogic;
using BusinessLogic.Connectivity.Entities;
using System.IO;
using System.Xml.Serialization;
using System.Collections.Generic;
using System.ServiceModel;
using System.Linq;
using System.Drawing;
using System.Xml;


namespace BankApplication.UI
{
	public partial class MainForm : Form
	{
		public MainForm()
		{
			InitializeComponent();
		}


		private void MainForm_Load(object sender, System.EventArgs e)
		{
			LoginForm loginForm = new LoginForm();
			var result = loginForm.ShowDialog();

			if (result == DialogResult.Cancel)
			{
				Application.Exit();
			}
		}


		private void DataGrid_PopupContextMenu(DataGridView dataGrid, ContextMenuStrip contextMenu, MouseEventArgs e)
		{
			DataGridView.HitTestInfo hittestInfo;

			if (e.Button == MouseButtons.Right)
			{
				hittestInfo = dataGrid.HitTest(e.X, e.Y);

				if (hittestInfo.Type == DataGridViewHitTestType.Cell)
				{
					if (!((DataGridViewRow)(dataGrid.Rows[hittestInfo.RowIndex])).Selected)
					{
						dataGrid.ClearSelection();

						((DataGridViewRow)dataGrid.Rows[hittestInfo.RowIndex]).Selected = true;
					}

					contextMenu.Show(dataGrid.PointToScreen(e.Location));
				}
			}
		}


		#region Users Tab 

		private void UpdateUsersList()
		{
			new ServerInteractionExceptionCatcher().Execute(() =>
				{
					users = UsersManager.GetAllUsers(SessionManager.Instance.SessionTicket);

					var usersToDisplay = 
						from user in users
						select new {
							user.Name,
							user.Surname,
							user.MiddleName,
							user.Phone,
							user.Fax,
							user.Email,
							user.Login,
						};

					UsersGrid.DataSource = usersToDisplay.ToList();
				});
		}

		private int GetSelectedUserID()
		{
			return users[UsersGrid.SelectedRows[0].Index].ID;
		}


		private void updateUsersGridButton_Click(object sender, EventArgs e)
		{
			UpdateUsersList();
		}


		private void CreateUserButton_Click(object sender, EventArgs e)
		{
			UserForm userForm = new UserForm();
			userForm.ShowDialog();

			UpdateUsersList();
		}


		private void clientsGrid_MouseClick(object sender, MouseEventArgs e)
		{
			DataGrid_PopupContextMenu((DataGridView)sender, UsersManagementContextMenu, e);
		}


		private void DeleteUserMenuItem_Click(object sender, EventArgs e)
		{
			DialogResult result = MessageBox.Show("Are you sure you want to delete this item? Please note, that all the accounts that the user owns must be deleted before doing this!",
				"Really delete?", MessageBoxButtons.YesNo, MessageBoxIcon.Question);

			if (result == DialogResult.Yes)
			{
				int userID = GetSelectedUserID();

				new ServerInteractionExceptionCatcher().Execute(() =>
					{
						UsersManager.DeleteUser(userID, SessionManager.Instance.SessionTicket);

						UpdateUsersList();
					});
			}
		}

		private void editUserToolStripMenuItem_Click(object sender, EventArgs e)
		{
			UserForm userForm = new UserForm(users[UsersGrid.SelectedRows[0].Index], UserFormType.UPDATE_USER);
			userForm.ShowDialog();

			UpdateUsersList();
		}

		#endregion


		#region Accounts tab 

		private void UpdateAccountsList()
		{
			new ServerInteractionExceptionCatcher().Execute(() =>
				{
					accounts = AccountsManager.GetAllAcounts(SessionManager.Instance.SessionTicket);
					var currencies = CurrenciesManager.GetCurrencies(SessionManager.Instance.SessionTicket);

					var users = UsersManager.GetAllUsers(SessionManager.Instance.SessionTicket);

					var usersByIdDictionary = new Dictionary<int, User>();
					foreach (var user in users)
					{
						usersByIdDictionary[user.ID] = user;
					}

					var accountsToDisplay =
						from account in accounts
						select new {
							account.Number,
							account.Balance,
							Accountee = usersByIdDictionary[account.Accountee.ID].Login,
							Currency = 
								(from currency in currencies
								 where currency.ID == account.Currency.ID
								 select currency.Name).ToList()[0]
						};

					AccountsDataGridView.DataSource = accountsToDisplay.ToList();
				});
		}


		private int GetSelectedAccountID()
		{
			return accounts[AccountsDataGridView.SelectedRows[0].Index].ID;
		}


		private void UpdateCurrencyConversionsGrid()
		{
			new ServerInteractionExceptionCatcher().Execute(() =>
			{
				var currencies = CurrenciesManager.GetCurrencies(SessionManager.Instance.SessionTicket);

				var currencyNames = new Dictionary<int, string>();
				foreach (var currency in currencies)
				{
					currencyNames[currency.ID] = currency.Name;
				}

				currencyConversions = CurrenciesManager.GetCurrencyConversions();

				var currencyConversionsToDisplay =
					(from conversion in currencyConversions
					select new CurrencyConversionView(
						currencyNames[conversion.SourceCurrency.ID],
						currencyNames[conversion.TargetCurrency.ID],
						conversion.ConversionRate)).ToList();

				CurrencyConversionsDataGrid.DataSource = currencyConversionsToDisplay;

				for (int numColumn = 0; numColumn < CurrencyConversionsDataGrid.ColumnCount - 1; ++numColumn)
				{
					CurrencyConversionsDataGrid.Columns[numColumn].ReadOnly = true;
				}
			});
		}


		private void UpdataAccountsGridButton_Click(object sender, EventArgs e)
		{
			UpdateAccountsList();
		}


		private void CreateAccountButton_Click(object sender, EventArgs e)
		{
			AccountForm accountForm = new AccountForm();
			accountForm.ShowDialog();

			UpdateAccountsList();
		}


		private void ReplenishAccountMenuItem_Click(object sender, EventArgs e)
		{
			int accountID = GetSelectedAccountID();

			var replenishAccountForm = new ReplenishAccountForm(accountID);
			replenishAccountForm.ShowDialog();

			UpdateAccountsList();
		}


		private void DeleteAccountMenuItem_Click(object sender, EventArgs e)
		{
			DialogResult result = MessageBox.Show("Are you sure you want to delete this item?",
				"Really delete?", MessageBoxButtons.YesNo, MessageBoxIcon.Question);

			if (result == DialogResult.Yes)
			{

				int accountID = GetSelectedAccountID();

				new ServerInteractionExceptionCatcher().Execute(() =>
				{
					AccountsManager.DeleteAccount(accountID, SessionManager.Instance.SessionTicket);

					UpdateAccountsList();
				});
			}
		}


		private void AccountsDataGridView_MouseClick(object sender, MouseEventArgs e)
		{
			DataGrid_PopupContextMenu((DataGridView)sender, AccountsManagementContextMenu, e);
		}


		#endregion


		#region Roles 

		private class UserRolePairView
		{
			public UserRolePairView(User user, Role role)
			{
				UserID = user.ID;
				UserLogin = user.Login;
				Role = role;
			}

			public int UserID { get; private set; }
			public string UserLogin { get; private set; }
			public Role Role { get; private set; }
		}


		public void UpdateUsersRolesGrid()
		{
			new ServerInteractionExceptionCatcher().Execute(() =>
				{
					var usersRoles = UsersManager.GetUsersRolesPairs(SessionManager.Instance.SessionTicket);

					userRolePairViews = new List<UserRolePairView>();

					foreach (var userRolePair in usersRoles)
					{
						foreach (var role in userRolePair.Value)
						{
							userRolePairViews.Add(new UserRolePairView(userRolePair.Key, role));
						}
					}

					RolesDataGrid.DataSource = userRolePairViews.Select(_ => new { _.UserLogin, _.Role }).ToList();
				});
		}


		private void UpdateUsersRolesButton_Click(object sender, EventArgs e)
		{
			UpdateUsersRolesGrid();
		}


		private void assignRoleButton_Click(object sender, EventArgs e)
		{
			var addRoleForm = new RoleForm();
			addRoleForm.ShowDialog();

			UpdateUsersRolesGrid();
		}


		private void DeleteUserRoleMenuItem_Click(object sender, EventArgs e)
		{
			DialogResult result = MessageBox.Show("Are you sure you want to delete this item?",
				"Really delete?", MessageBoxButtons.YesNo, MessageBoxIcon.Question);

			if (result == DialogResult.Yes)
			{
				var selectedUserRolePairIndex = RolesDataGrid.SelectedRows[0].Index;

				new ServerInteractionExceptionCatcher().Execute(() =>
				{
					UsersManager.DeleteRole(userRolePairViews[selectedUserRolePairIndex].UserID,
						userRolePairViews[selectedUserRolePairIndex].Role,
						SessionManager.Instance.SessionTicket);
				});

				UpdateUsersRolesGrid();
			}
		}


		private void RolesDataGrid_MouseClick(object sender, MouseEventArgs e)
		{
			DataGrid_PopupContextMenu((DataGridView)sender, UsersRolesGridContextMenu, e);
		}

		#endregion


		#region Currencies

		public void UpdateCurrenciesGrid()
		{
			new ServerInteractionExceptionCatcher().Execute(() =>
			{
				currencies = CurrenciesManager.GetCurrencies(SessionManager.Instance.SessionTicket);
				var currenciesViews = (from currency in currencies select new { Code = currency.Code, Name = currency.Name });

				CurrenciesDataGridView.DataSource = currenciesViews.ToList();
			});
		}


		private int GetSelectedCurrencyID()
		{
			return currencies[CurrenciesDataGridView.SelectedRows[0].Index].ID;
		}


		private void AddCurrencyButton_Click(object sender, EventArgs e)
		{
			var addCurrencyForm = new CurrencyForm();
			addCurrencyForm.ShowDialog();

			UpdateCurrenciesGrid();
			UpdateCurrencyConversionsGrid();
		}


		private void UpdateCurrenciesListButton_Click(object sender, EventArgs e)
		{
			UpdateCurrenciesGrid();
		}


		private void DeleteCurrencyMenuItem_Click(object sender, EventArgs e)
		{
			DialogResult result = MessageBox.Show("Are you sure you want to delete this item? Please note, that all the currency conversions that involve this currency must be deleted before doing this!",
				"Really delete?", MessageBoxButtons.YesNo, MessageBoxIcon.Question);

			if (result == DialogResult.Yes)
			{
				int currencyID = GetSelectedCurrencyID();

				new ServerInteractionExceptionCatcher().Execute(() =>
				{
					CurrenciesManager.DeleteCurrency(currencyID, SessionManager.Instance.SessionTicket);
				});

				UpdateCurrenciesGrid();
			}
		}


		private void CurrenciesDataGridView_MouseClick(object sender, MouseEventArgs e)
		{
			DataGrid_PopupContextMenu((DataGridView)sender, CurrencyContextMenuStrip, e);
		}

		#endregion


		#region Currency conversions

		private class CurrencyConversionView
		{
			public CurrencyConversionView()
			{ }

			public CurrencyConversionView(
				String sourceCurrency,
				String destinationCurrency,
				decimal conversionRate)
			{
				SourceCurrency = sourceCurrency;
				DestinationCurrency = destinationCurrency;
				ConversionRate = conversionRate;
			}

			public String SourceCurrency { get; set; }
			public String DestinationCurrency { get; set; }
			public Decimal ConversionRate { get; set; }
		}


		private int GetSelectedCurrencyConversionID()
		{
			return currencyConversions[CurrencyConversionsDataGrid.SelectedRows[0].Index].ID;
		}


		private void AddCurrencyConversionButton_Click(object sender, EventArgs e)
		{
			var addCurrencyConversionForm = new CurrencyConversionForm();
			addCurrencyConversionForm.ShowDialog();

			UpdateCurrencyConversionsGrid();
		}


		private void CurrencyConversionsDataGrid_MouseClick(object sender, MouseEventArgs e)
		{
			DataGrid_PopupContextMenu((DataGridView)sender, CurrencyConversionscontextMenuStrip, e);
		}


		private void DeleteCurrencyConversionMenuItem_Click(object sender, EventArgs e)
		{
			DialogResult result = MessageBox.Show("Are you sure you want to delete this item?",
				"Really delete?", MessageBoxButtons.YesNo, MessageBoxIcon.Question);

			if (result == DialogResult.Yes)
			{
				int currencyConversionID = GetSelectedCurrencyConversionID();

				new ServerInteractionExceptionCatcher().Execute(() =>
				{
					CurrenciesManager.DeleteCurrencyConversion(currencyConversionID, SessionManager.Instance.SessionTicket);
				});

				UpdateCurrencyConversionsGrid();
			}
		}


		private void UpdateCurrencyConversions_Click(object sender, EventArgs e)
		{
			UpdateCurrencyConversionsGrid();
		}


		private void currencyConversionsDataGrid_CellEndEdit(object sender, DataGridViewCellEventArgs e)
		{
			var dataGrid = (DataGridView)sender;
			int numConversionRateColumn = dataGrid.ColumnCount - 1;

			if (e.ColumnIndex == numConversionRateColumn)
			{
				try
				{
					string cellContents = CurrencyConversionsDataGrid[e.ColumnIndex, e.RowIndex].Value.ToString();
					Decimal newValue = Convert.ToDecimal(cellContents);

					new ServerInteractionExceptionCatcher().Execute(() =>
					{
						var conversionToUpdate = currencyConversions[e.RowIndex];
						conversionToUpdate.ConversionRate = newValue;
						CurrenciesManager.UpdateCurrencyConversion(conversionToUpdate, SessionManager.Instance.SessionTicket);
					});
				}
				catch (Exception)
				{
					MessageBox.Show("Wrong currency conversion rate! Input a valid positive floating point value (with ',') e.g. 11,4342 and press 'Enter' to save it or 'Esc' to cancel.",
										"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
				}
			}
		}


		private void currencyConversionsDataGrid_DataError(object sender, DataGridViewDataErrorEventArgs e)
		{
			var dataGrid = (DataGridView)sender;
			int numConversionRateColumn = dataGrid.ColumnCount - 1;

			if (e.ColumnIndex == numConversionRateColumn)
			{
				MessageBox.Show("Wrong currency conversion rate! Input a valid positive floating point value (with ',') e.g. 11,4342 and press 'Enter' to save it or 'Esc' to cancel.",
										"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

		// -------------------
		private void CurrencyConversionsDataGrid_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
		{
			var dataGrid = (DataGridView)sender;
			int numConversionRateColumn = dataGrid.ColumnCount - 1;

			if (dataGrid.CurrentCell.ColumnIndex == numConversionRateColumn)
			{
				TextBox txtBox = e.Control as TextBox;
				txtBox.KeyPress += new KeyPressEventHandler(txtBox_KeyPress);
			}
		}

		private void txtBox_KeyPress(object sender, KeyPressEventArgs e)
		{
			if (e.KeyChar == '-')
			{
// 				MessageBox.Show("Wrong currency conversion rate! Input a valid positive floating point value (with ',') e.g. 11,4342 and press 'Enter' to save it or 'Esc' to cancel.",
// 										"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

				e.Handled = true;
			}
			else
			{
				e.Handled = false;
			}
		}

		// -------------------


		#endregion


		#region Main menu 


		private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
		{
			AboutBox aboutBox = new AboutBox();
			aboutBox.ShowDialog();
		}


		private void quitToolStripMenuItem_Click(object sender, EventArgs e)
		{
			DialogResult result = MessageBox.Show("Are you sure you want to quit!",
				"Really quit?", MessageBoxButtons.YesNo, MessageBoxIcon.Question);

			if (result == DialogResult.Yes)
			{
				Application.Exit();
			}
		}


		private void reloginToolStripMenuItem_Click(object sender, EventArgs e)
		{
			//DialogResult result = MessageBox.Show("Are you sure you want to relogin!",
			//    "Really relogin?", MessageBoxButtons.YesNo, MessageBoxIcon.Question);

			//if (result == DialogResult.Yes)
			//{
				var loginForm = new LoginForm();
				loginForm.ShowDialog();
			//}
		}


		private void regenerateKeysToolStripMenuItem_Click(object sender, EventArgs e)
		{
			try
			{
				RSAKeys keysPair = RSADigitalSignatureManager.GenerateRSAKeys();

				int userID = GetSelectedUserID();

				MiscUtils.SerializeToXML(keysPair.PrivateKey, MiscUtils.GetSaveFileName("Save private key to ..."));

				new ServerInteractionExceptionCatcher().Execute(() =>
				{
					UsersManager.SetUserPublicKey(userID, keysPair.PublicKey, SessionManager.Instance.SessionTicket);

					UpdateUsersList();
				});
			}
			catch (NoFileNameSpecifiedException)
			{
				MessageBox.Show("No file name has been specified!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
			}
			catch (IOException)
			{
				MessageBox.Show("File operation failed!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
			catch (Exception)
			{
				MessageBox.Show("Unknown error has been encountered! Terminating...", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
				Application.Exit();
			}
		}

		#endregion


		#region RAs view 

		class RAView
		{
			public RAView(
				String RaNumber,
				String Description, 
				decimal Price,
				string BillToPartyAccount,
				string PayerAccount,
				string Status,
				string Currency,
				DateTime IssueDate,
				string RejectionReason)
			{
				this.RaNumber = RaNumber;
				this.Description = Description;
				this.Price = Price;
				this.BillToPartyAccount = BillToPartyAccount;
				this.PayerAccount = PayerAccount;
				this.Status = Status;
				this.Currency = Currency;
				this.IssueDate = IssueDate;
				this.RejectionReason = RejectionReason;
			}



			public String RaNumber { get; set; }
			public String Description { get; set; }

			public decimal Price { get; set; }

			public string BillToPartyAccount { get; set; }
			public String PayerAccount { get; set; }

			public String Status { get; set; }
			public String Currency { get; set; }
			public DateTime IssueDate { get; set; }
			public String RejectionReason { get; set; }
		}

		private void displayRAsButton_Click(object sender, EventArgs e)
		{
			string userName = UserCombobox.Text;
			DateTime periodStart = dateTimePickerStart.Value;
			DateTime periodEnd = dateTimePickerEnd.Value;

			new ServerInteractionExceptionCatcher().Execute(() =>
			{
				var envelopes = AccountsManager.GetFilteredEnvelopes(userName, periodStart, periodEnd, SessionManager.Instance.SessionTicket);

				List<RAView> raViews = new List<RAView>();

				try
				{
					foreach (var envelope in envelopes)
					{
							var serializer = new XmlSerializer(typeof(RemittanceAdvice));
							XmlReader reader = XmlReader.Create(new StringReader(envelope.DocXML));

							if (serializer.CanDeserialize(reader))
							{
								RemittanceAdvice ra = (RemittanceAdvice)serializer.Deserialize(reader);

								string rejectionReason = string.Empty;
								if (envelope.Status == DocumentEnvelopeStatus.NotAccepted)
								{
									rejectionReason = envelope.RejectionReason;
								}
								else
								{
									rejectionReason = string.Empty;
								}

								var raView = new RAView(
									ra.Number,
									ra.Description,
									ra.TotalAmount,
									ra.BillToPartyAccount.Number,
									ra.PayerPartyAccount.Number,
									envelope.Status.ToString(),
									ra.Currency.Name,
									envelope.SentDate,
									rejectionReason);

								raViews.Add(raView);
							}
							else
							{
								var raView = new RAView(
									string.Empty,
									string.Empty,
									default(decimal),
									string.Empty,
									string.Empty,
									envelope.Status.ToString(),
									string.Empty,
									envelope.SentDate,
									envelope.RejectionReason);

								raViews.Add(raView);
							}

					}
				}
				catch (Exception ex)
				{
					// ...
				}
				

				RAsGrid.DataSource = raViews;
			});
		}

		private void UserCombobox_DropDown(object sender, EventArgs e)
		{
			new ServerInteractionExceptionCatcher().Execute(() =>
			{
				users = UsersManager.GetAllUsers(SessionManager.Instance.SessionTicket);
				var userLogins = users.Select(user => user.Login);
				UserCombobox.DataSource = userLogins.ToList();
			});
		}

		#endregion

		private IList<User> users;
		private IList<Account> accounts;
		private List<UserRolePairView> userRolePairViews;
		private IList<Currency> currencies;
		private IList<CurrencyConversion> currencyConversions;

		



	}
}
